package ws

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"gogame/logger"
	"net/http"
	"runtime/debug"
	"time"
)

// WebsocketInterFace websocket服务需要实现的方法
type WebsocketInterFace interface {
	OnMessage(packet []byte)     // 接收数据
	OnClose()                    // 连接断开
	OnOpen(conn *websocket.Conn) // 连接到来
	IsStop() bool                // 服务是否停止
}

// websocket升级
var upGrader = websocket.Upgrader{
	// 是否跨域
	CheckOrigin: func(request *http.Request) bool {
		return true
	},
}

type WebSocketHandle func(*http.Request) WebsocketInterFace

// WebsocketServer websocket服务
type WebsocketServer struct {
	listener        *gin.Engine
	httpServer      *http.Server
	Path2WsHandle   map[string]WebSocketHandle
	Path2HttpHandle map[string]gin.HandlerFunc
}

func NewGin() *gin.Engine {
	gin.SetMode(gin.ReleaseMode)

	//engine := gin.Default()
	// 这里不用Default是为了不使用gin自带的日志
	engine := gin.New()
	engine.Use(gin.Recovery())

	return engine
}

func (s *WebsocketServer) Listen(addr string) (err error) {
	s.listener = NewGin()
	// ws
	for path, handleFunc := range s.Path2WsHandle {
		s.listener.GET(path, func(ctx *gin.Context) {
			ws, err := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)
			if err != nil {
				_, _ = ctx.Writer.Write([]byte(fmt.Sprintf(`{"error":%s}`, err.Error())))
				return
			} else {
				go Handle(ws, ctx.Request, handleFunc)
			}
		})
	}

	// http
	for path, handle := range s.Path2HttpHandle {
		s.listener.GET(path, handle)
	}

	s.httpServer = &http.Server{
		Addr:    addr,
		Handler: s.listener,
	}

	//return s.listener.Run(addr)
	return s.httpServer.ListenAndServe()
}

// Handle 游戏逻辑websocket 处理
func Handle(conn *websocket.Conn, request *http.Request, getGateWayApp func(*http.Request) WebsocketInterFace) {
	defer func() {
		if result := recover(); result != nil {
			logger.GatewayLog.Warn(fmt.Sprintf("ws.Handle error-->%s\n%s", result.(error).Error(), debug.Stack()))
		}
	}()

	var (
		t   int
		err error
		msg []byte
	)
	GateWayApp := getGateWayApp(request)
	defer GateWayApp.OnClose()

	GateWayApp.OnOpen(conn)
	// 消息接收
	for !GateWayApp.IsStop() {
		if t, msg, err = conn.ReadMessage(); err != nil || t == -1 {
			break
		} else {
			GateWayApp.OnMessage(msg)
		}
	}
}

func (s *WebsocketServer) Close() {
	_ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
	s.httpServer.Shutdown(_ctx)
}
